package com.hobart.learning.lagou.edu.factory;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: Hobart Mr.Bai
 * @create: 2021-02-01 16:16
 * @description: bean 工厂
 * @Version 1.0
 **/
public class BeanFactory {
    // 第一步，读取xml， 通过反射技术实例化对象，并且存储进Map 集合
    private static Map<String, Object> map = new HashMap<>();

    /**
     * XPath 使用路径表达式来选取 XML 文档中的节点或节点集
     *  nodename  选取此节点的所有的子节点
     *  /       从根节点选取
     *  //      从匹配选择的当前节点选择文档中的节点。而不考虑他们的位置
     *  *       选取当前节点
     *  **      选择当前节点的父节点
     *  @       选取属性
     */
    static {
        // 加载 bean.xml
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();
            List<Element> BeanList = rootElement.selectNodes("//bean");
            for (Element element : BeanList) {
                // 处理每个bean元素，获取到该元素的id和class属性
                String id = element.attributeValue("id");
                String clazz = element.attributeValue("class");
                // 通过反射 实例化 对象
                Class<?> aClass = Class.forName(clazz);
                // 实例化的对象
                Object o = aClass.newInstance();
                
                // 实例化的对象 存储到 map 中
                map.put(id, o);
            }

            // 实例化完成后 维护 对象的 依赖关系， 检查那些对象需要传入参数，
            // 根据bean.xml中的配置，传入传入相应的参数
            // 有property 元素的bean 就需要传入参数

            List<Element> propertyList = rootElement.selectNodes("//property");
            // 解析 property， 获取元素
            for (Element element : propertyList) {
                String name = element.attributeValue("name");
                String ref = element.attributeValue("ref");

                // 找到当前需要被处理依赖关系的bean
                Element parent = element.getParent();

                // 调用父类元素对象的反射功能
                String parentId = parent.attributeValue("id");
                Object parentObject = map.get(parentId);

                //遍历父对象中的所有方法， 找到"set" + name 的方法
                Class<?> parentObjectClass = parentObject.getClass();
                Method[] methods = parentObjectClass.getMethods();
                for (Method method : methods) {
                    // str.substring(0,1).toUpperCase()+str.substring(1)
                    if(method.getName().equalsIgnoreCase("set"+name)){
                        method.invoke(parentObject, map.get(ref));
                    }

                }

                // 把处理后的 map 重新放入到 map 中
                map.put(parentId, parentObject);

            }

        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    // 第二步，对外提供获取实例对象的接口
    public static Object getBean(String id) {
        return map.get(id);
    }
}
